package pers.yurwisher.rubick.gateway.support;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import pers.yurwisher.rubick.common.enums.tip.CustomTipEnum;
import pers.yurwisher.rubick.common.exception.CustomException;
import pers.yurwisher.rubick.common.wrapper.R;
import pers.yurwisher.rubick.gateway.exception.GatewayCustomTipEnum;
import reactor.core.publisher.Mono;

import java.net.ConnectException;
import java.util.Map;

/**
 * @author yq
 * @date 2019/01/21 14:09
 * @description 网关全局异常回调
 * @since V1.0.0
 */
@Slf4j
public class JsonExceptionHandler extends DefaultErrorWebExceptionHandler {

    public JsonExceptionHandler(ErrorAttributes errorAttributes,
                                ResourceProperties resourceProperties,
                                ErrorProperties errorProperties,
                                ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    }

    /**
     * 异常信息包装
     */
    @Override
    protected Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
        Throwable error = super.getError(request);
        R r;
        ServerWebExchange exchange = request.exchange();
        log.info("请求ID:[{}],异常[{}]", exchange.getRequest().getId(), error.getLocalizedMessage());
        if (error instanceof NotFoundException) {
            r = R.fail(GatewayCustomTipEnum.SERVICE_404);
        } else if (error instanceof CustomException) {
            r = R.fail(((CustomException) error).getTip());
        } else if (error instanceof ResponseStatusException) {
            ResponseStatusException exception = (ResponseStatusException) error;
            //服务404
            if (HttpStatus.NOT_FOUND == exception.getStatus()) {
                r = R.fail(GatewayCustomTipEnum.SERVICE_404);
            } else if (HttpStatus.INTERNAL_SERVER_ERROR == exception.getStatus()) {
                //服务500
                r = R.fail(GatewayCustomTipEnum.SERVICE_500);
            } else if (HttpStatus.GATEWAY_TIMEOUT == exception.getStatus()) {
                //服务接口超时
                r = R.fail(GatewayCustomTipEnum.SERVICE_504);
            } else {
                r = R.fail(exception.getLocalizedMessage());
            }
        } else if(error instanceof ConnectException){
            r = R.fail(GatewayCustomTipEnum.SERVICE_500_2);
        }else{
            r = R.fail(error.getLocalizedMessage());
        }
        return (JSONObject) JSONObject.toJSON(r);
    }

    /**
     * 响应结果转为json
     */
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    @Override
    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        Map<String, Object> error = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
        return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(error));
    }
}
